
#include "hy_net_base.h"
#include "hy_socketops.h"
#include "hy_handle_accept.h"


hy_handle_accept::hy_handle_accept() 
{

}

hy_handle_accept::~hy_handle_accept()
{
	close();
}

hy_err_t hy_handle_accept::init( hy_reactor_base* reactor,hy_accept_tactics* accept )
{
	_reactor	= reactor;
	_accept		= accept;
	return hy_right;
}
hy_err_t hy_handle_accept::iocp_open()
{
#ifdef _WIN32
	DWORD dwBytes = 0;  
	if(SOCKET_ERROR == WSAIoctl(_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, 
		&GuidAcceptEx, sizeof(GuidAcceptEx), 
		&lpfnAcceptEx, sizeof(lpfnAcceptEx), 
		&dwBytes, NULL, NULL))  
	{  
		hy_trace("WSAIoctl failed with error code: %d/n", WSAGetLastError());  
		if(INVALID_SOCKET != _socket)  
		{  
			close();  
		}  
		return hy_error;
	}  

	if(SOCKET_ERROR == WSAIoctl(_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, 
		&GuidGetAcceptExSockAddrs, sizeof(GuidGetAcceptExSockAddrs), 
		&lpfnGetAcceptExSockAddrs, sizeof(lpfnGetAcceptExSockAddrs),   
		&dwBytes, NULL, NULL))  
	{  
		hy_trace("WSAIoctl failed with error code: %d/n", WSAGetLastError());  
		if(INVALID_SOCKET != _socket)  
		{  
			close();  
		}  
		return hy_error;
	}  

	// Post MAX_ACCEPT accept
	for(int i=0; i<c_pre_sockets; i++)
	{
		hy_accept_iocp_data* data = new hy_accept_iocp_data();
		_sockets[i] = data;
		post_accept(i);
	}
#endif
	return hy_right;
}

hy_err_t hy_handle_accept::open( const char* addr,hy_u32_t port )
{
	hy_assert((_socket == HY_INVALID_SOCKET));
	if (_socket != HY_INVALID_SOCKET)
	{
		//already open and listen
		return hy_error;
	}
	hy_err_t ec = hy_right;
	if ( ( ec = hy_os::socket(SOCK_STREAM,_socket)) == hy_right)
	{
		int opt = 1;
		if ((ec = hy_os::setsockopt(_socket,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) == hy_right)
		{
			if ( (ec = hy_os::setnonblocking(_socket)) == hy_right)
			{
				if ((ec = hy_os::bind(_socket,addr,port)) == hy_right)
				{
					if ((ec = hy_os::listen(_socket)) == hy_right)
					{
						hy_trace("open socket listen success [%s][%d]",addr,port);
						if (iocp_open() == hy_right)
						{
							_reactor->checkin(this);
							return hy_right;
						}
						else
						{
							close();
							return hy_error;
						}
					}
				}
			}
		}
		hy_os::close(_socket);	
	}
	hy_trace("open socket error [%s][%d]",addr,port);

	_socket = HY_INVALID_SOCKET;
	return ec;
}

void hy_handle_accept::handle_input( hy_i32_t bytes )
{
	hy_trace("new accept handle input");
#ifdef _WIN32
	if (bytes < 0)
	{
		return;
	}
	hy_assert(bytes>=0 && bytes < c_pre_sockets);
	hy_accept_iocp_data* pIoData = _sockets[bytes];

	SOCKADDR_IN* remote = NULL;  
	SOCKADDR_IN* local = NULL;  
	int remoteLen = sizeof(SOCKADDR_IN);  
	int localLen = sizeof(SOCKADDR_IN);  
	lpfnGetAcceptExSockAddrs(pIoData->_addr, sizeof(pIoData->_addr) - ((sizeof(SOCKADDR_IN)+16)*2),  
		sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16
		,(LPSOCKADDR*)&local, &localLen, (LPSOCKADDR*)&remote, &remoteLen);  
	hy_trace("Client <%s : %d> come in.", inet_ntoa(remote->sin_addr),ntohs(remote->sin_port));  
	//hy_trace("Recv Data: <%s : %d> %s.", inet_ntoa(remote->sin_addr),ntohs(remote->sin_port), pIoData->_addr);  
	_accept->handle_accept(pIoData->_accept);
	post_accept(bytes);  
#else
	hy_socket_t new_sc = HY_INVALID_SOCKET;
	while (hy_os::accept(_socket,new_sc) == hy_right)
	{
		_accept->handle_accept(new_sc);
	}
#endif
}

void hy_handle_accept::close()
{
	if (_socket != HY_INVALID_SOCKET)
	{
		_reactor->checkout(this);
		hy_os::close(_socket);
		_socket = HY_INVALID_SOCKET;
#ifdef _WIN32
		//receycle _sockets res
		for(int i = 0; i < c_pre_sockets; i++)
		{
			if (_sockets[i] != NULL)
			{
				hy_os::close(_sockets[i]->_accept);
				delete _sockets[i];
				_sockets[i] = NULL;
			}
		}
#endif
	}
}

void hy_handle_accept::handle_error()
{
	close();
}


hy_err_t hy_handle_accept::post_accept( int index )
{
#ifdef _WIN32
	hy_assert(index>=0 && index < c_pre_sockets);
	hy_accept_iocp_data* pIoData = _sockets[index];
	pIoData->reset();
	pIoData->hb = this;
	pIoData->_index = index;
	pIoData->event = hie_accept;

	DWORD dwBytes = 0;  
	pIoData->_accept = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);  
	if(INVALID_SOCKET == pIoData->_accept)  
	{  
		hy_trace("WSASocket failed with error code: %d/n", WSAGetLastError());  
		return hy_error;  
	}  

	if(FALSE == lpfnAcceptEx(_socket, pIoData->_accept 
		,pIoData->_addr, sizeof(pIoData->_addr) - ((sizeof(SOCKADDR_IN)+16)*2)
		,sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16
		,&dwBytes, &(pIoData->ol)))  
	{  
		if(WSA_IO_PENDING != WSAGetLastError())  
		{  
			hy_trace("lpfnAcceptEx failed with error code: %d/n", WSAGetLastError());  

			return hy_error;  
		}  
	} 
#endif
	return hy_right;
}

void hy_handle_accept::handle_output( hy_i32_t bytes )
{

}